using System;
using System.Collections;
using System.Xml;
using System.Xml.XPath;
using MessageGateway;

namespace Router
{
    /* 
     * Notifies interested parties where a message went. 0 based index.
     */
    public delegate void OnRouterEvent (XmlDocument msg, int pathChosen);

    /*
     * Routes an incoming message to one of multiple specified output channels.
     * The first channels whose associated xpath expression is true receives the
     * message. An invalid XPath expression is treated the same as "false".
     * 
     * The constructor expects a IList of MessageDestination instances.
     */
    public class RouterComponent
    {
        protected IMessageReceiver input;
        protected IList destinations = new ArrayList ();

        protected OnRouterEvent routerEvent;

        public RouterComponent (IMessageReceiver inputQueue, IList destinations)
        {
            this.input = inputQueue;
            this.destinations = destinations;

            routerEvent = new OnRouterEvent (NullEvent);
            input.AddEventHandler (new OnMsgEvent (OnMessage));
            input.Activate ();
        }

        public void AddEventHandler(OnMsgEvent handler)
        {
            input.AddEventHandler(handler);
        }

        public void AddEventHandler (OnRouterEvent handler)
        {
            routerEvent += handler;
        }

        private void NullEvent (XmlDocument message, int pathChosen)
        {
        }

        protected void OnMessage (XmlDocument message)
        {
            XPathNavigator nav = message.CreateNavigator ();
            foreach (MessageDestination destination in destinations)
            {
                try
                {
                    Object result = nav.Evaluate (destination.XPathExpression);

                    if (result is Boolean && (bool) result == true)
                    {
                        destination.DestinationChannel.Send (message);
                        routerEvent (message, destinations.IndexOf (destination));
                        break;
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.Message);
                    // do nothing
                }
            }
        }

    }

    public class MessageDestination
    {
        private string xPathExpression;
        private IMessageSender destinationChannel;

        public MessageDestination (string xPathExpression, IMessageSender destinationChannel)
        {
            this.xPathExpression = xPathExpression;
            this.destinationChannel = destinationChannel;
        }

        public MessageDestination (IMessageSender destinationChannel) : this ("true()", destinationChannel)
        {
        }

        public string XPathExpression
        {
            get { return xPathExpression; }
        }
        public IMessageSender DestinationChannel
        {
            get { return destinationChannel; }
        }
    }
}